﻿#include "stdafx.h"
#if _QG_USE_DX9
#include "qg/qgasv.h"
#include "qg_dx9_dev.h"
#include "qg_dx9_cnv.h"
#include "qg_dx9_mtrl.h"
#include "qg_trace.h"
#include "qg_math.h"

// 스크린삿
qgImage* DX9Device::ScreenCapture()
{
	return NULL;
}

//
bool DX9Device::_CommitTexture(kint stage, qgTexture* tex) throw()
{
	if (tex)
	{
		DX9Texture* dtex = (DX9Texture*)tex;
		IDirect3DBaseTexture9* tx = (IDirect3DBaseTexture9*)dtex->GetDescription();

		_device->SetTexture((DWORD)stage, tx);

		return true;
	}
	else
	{
		_device->SetTexture((DWORD)stage, NULL);

		return false;
	}
}

//
qgTexture* DX9Device::_RoamImplTexture(const qgArprTexture& ap)
{
	if (K_OFLAG(ap.flags, QGTEXF_OFF))
	{
		DX9TextureOff* off = kobj_create(DX9TextureOff);

		if (!off->_InitProperty(ap.type, ap.name, ap.width, ap.height, ap.depth, ap.mips, ap.flags, ap.fmt, this) ||
			!off->_CreateTexture(NULL, this) ||
			!off->_CreateOff(this))
		{
			off->Unload();
			off = NULL;
		}

		return off;
	}
	else
	{
		DX9Texture* tex = kobj_create(DX9Texture);

		if (!tex->_InitProperty(ap.type, ap.name, ap.width, ap.height, ap.depth, ap.mips, ap.flags, ap.fmt, this) ||
			!tex->_CreateTexture(NULL, this))
		{
			tex->Unload();
			tex = NULL;
		}

		return tex;
	}
}

//
qgTexture* DX9Device::_RoamImplTexture(const qgArprImage& ap)
{
	DX9Texture* tex = kobj_create(DX9Texture);
	qgImage* img = ap.img;

	if (!tex->_InitImage(ap.name, img, 0, ap.flags, this) ||
		!tex->_CreateTexture(img, this))
	{
		tex->Unload();
		tex = NULL;
	}

	return tex;
}

//
qgTexture* DX9Device::_RoamImplMotionTexture(const char* filename, kint flags)
{
	DX9TextureMotion* tex = kobj_create(DX9TextureMotion);

	if (!tex->_CreateMotion(filename, flags, this))
	{
		tex->Unload();
		tex = NULL;
	}

	return tex;
}

//
qgDepthBuffer* DX9Device::_RoamImplDepthBuffer(const tpl::Point& size)
{
	DX9DepthBuffer* buf = kobj_create(DX9DepthBuffer);

	if (!buf->_Create(size.width, size.height, this))
	{
		buf->Unload();
		buf = NULL;
	}

	return buf;
}


//////////////////////////////////////////////////////////////////////////
// 텍스쳐
KIMPL_SUBCLASS(DX9Texture, "DX9Texture");

//
DX9Texture::DX9Texture() throw()
: _dx_fmt(D3DFMT_UNKNOWN)
{
}

//
DX9Texture::~DX9Texture() throw()
{
	if (_node_desc)
	{
		IDirect3DBaseTexture9* btx = (IDirect3DBaseTexture9*)_node_desc;
		btx->Release();
	}

	kobj_unload(_img_rsw);
	kobj_unload(_img_mip);
}

//
bool DX9Texture::_InitImage(const char* name, qgImage* img, kint mips, kint flags, DX9Device* dev)
{
	kint width = img->GetWidth();
	kint height = img->GetHeight();
	kint depth = 1;
	qgColorFormat fmt = img->GetColorFormat();

	return _InitProperty(QGTEXTURE_2D, name, width, height, depth, mips, flags, fmt, dev);
}

//
bool DX9Texture::_InitProperty(qgTextureType type, const char* name, kint width, kint height, kint depth, kint mips, kint flags, qgColorFormat fmt, DX9Device* dev)
{
	_key = name ? k_strdup(name) : k_apsprintf("dx9_texture_%Lu", k_nth());
	_textype = type;
	_flags = flags;
	_mips = mips;
	_fmt = fmt == QGCF_NONE ? QGCF32_RGBA : fmt;

	// 포맷 맞춤
	_dx_fmt = DX9Convert::DetectColor(_fmt, _alpha);
	if (_dx_fmt == D3DFMT_UNKNOWN)
	{
		// 지원하지 못하는 포맷
		return false;
	}

	if (K_OFLAG(flags, QGRESF_DYNAMIC))
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			// 다이나믹 + 오프 금지
			// DX9는 실제로 다이나믹일 껄
			return false;
		}

		if (!K_OFLAG(flags, QGTEXF_NOMIP))
		{
			// 다이나믹 -> 밉맵 금지
			K_YFLAG(_flags, QGTEXF_NOMIP, true);
		}

		_mips = 1;
		_dx_usage = D3DUSAGE_DYNAMIC;
		_dx_pool = D3DPOOL_DEFAULT;
	}
	else
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			if (!K_OFLAG(flags, QGTEXF_NOMIP))
			{
				// 오프 -> 밉맵 금지
				K_YFLAG(_flags, QGTEXF_NOMIP, true);
			}

			K_YFLAG(_insts, QGTEXI_OFF, true);
			_mips = 1;
			_dx_usage = D3DUSAGE_RENDERTARGET;
			_dx_pool = D3DPOOL_DEFAULT;
		}
		else
		{
			if (!K_OFLAG(flags, QGTEXF_NOMIP))
			{
				D3DRESOURCETYPE rtype = DX9Convert::TexToRtype(type);

				if (dev->TestEnableD3DFORMAT(_dx_fmt, D3DUSAGE_AUTOGENMIPMAP, rtype))
				{
					K_YFLAG(_insts, QGTEXI_AUTOMIP, true);
					_mips = 0;
					_dx_usage = D3DUSAGE_AUTOGENMIPMAP;
				}
				else
				{
					// 수동 밉맵은 지원안하니깐 밉맵은 1개
					_mips = 1;
				}
			}
			else
			{
				if (_mips == 0)
					_mips = 1;
			}

			if (!dev->IsEx())
				_dx_pool = D3DPOOL_MANAGED;
			else
			{
				_dx_usage |= D3DUSAGE_DYNAMIC;
				_dx_pool = D3DPOOL_DEFAULT;
			}
		}
	}

	// 최대 크기로 맞춤
	kint maxsize = dev->GetCaps().max_texture_dimension;

	if (width < maxsize)
		_width = width;
	else
	{
		_width = maxsize;
		K_YFLAG(_insts, QGTEXI_CLAMP, true);
	}

	if (height < maxsize)
		_height = height;
	else
	{
		_height = maxsize;
		K_YFLAG(_insts, QGTEXI_CLAMP, true);
	}

	if (depth < maxsize)
		_depth = depth;
	else
	{
		_depth = maxsize;
		K_YFLAG(_insts, QGTEXI_CLAMP, true);
	}

	// 정방 맞춤
	// ... 원래 어지간한 DX9 드라이버는 다 지원하지만, 혹시 모르므로
	if (dev->GetCaps().supp_texture_npo2)
	{
		_awidth = _width;
		_aheight = _height;
		_adepth = _depth;
	}
	else
	{
		_awidth = _math::OptimizeTextureSize(_width, true, 0);
		_aheight = _math::OptimizeTextureSize(_height, true, 0);
		_adepth = _math::OptimizeTextureSize(_depth, true, 0);

		if (_awidth != _width || _aheight != _height /*|| _adepth != _depth*/)
			K_YFLAG(_insts, QGTEXI_ADJPOW2, true);
	}

	// 모션
	if (K_OFLAG(flags, QGTEXF_MOTION))
		K_YFLAG(_insts, QGTEXI_MOTION, true);

	return true;
}

//
bool DX9Texture::_CreateInternal(IDirect3DBaseTexture9** ptex, DX9Device* dev)
{
	IDirect3DDevice9* device = dev->GetDevice();

	// 텍스쳐 만들기
	HRESULT hr;

	switch (_textype)
	{
		case QGTEXTURE_1D:
		case QGTEXTURE_2D:
			if (FAILED(hr = device->CreateTexture(_awidth, _aheight, _mips, _dx_usage, _dx_fmt, _dx_pool, (IDirect3DTexture9**)ptex, NULL)))
			{
				kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
				return false;
			}

			if (_mips == 0)
				_mips = (kint)(*(IDirect3DTexture9**)ptex)->GetLevelCount();

			break;

		case QGTEXTURE_3D:
			if (FAILED(hr = device->CreateVolumeTexture(_awidth, _aheight, _adepth, _mips, _dx_usage, _dx_fmt, _dx_pool, (IDirect3DVolumeTexture9**)ptex, NULL)))
			{
				kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
				return false;
			}

			if (_mips == 0)
				_mips = (kint)(*(IDirect3DVolumeTexture9**)ptex)->GetLevelCount();

			break;

		case QGTEXTURE_CUBE:
			if (FAILED(hr = device->CreateCubeTexture(_awidth, _mips, _dx_usage, _dx_fmt, _dx_pool, (IDirect3DCubeTexture9**)ptex, NULL)))
			{
				kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
				return false;
			}

			if (_mips == 0)
				_mips = (kint)(*(IDirect3DCubeTexture9**)ptex)->GetLevelCount();

			break;
	}

	return true;
}

//
bool DX9Texture::_CreateTexture(qgImage* img, DX9Device* dev)
{
	IDirect3DBaseTexture9* tex = NULL;

	if (!img)
	{
		// 빈 텍스쳐 만들기
		if (!_CreateInternal(&tex, dev))
			return false;

		_node_desc = (kuintptr)tex;
	}
	else
	{
		// 이미지처리
		_img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
		K_OFLAG(_insts, QGTEXI_ADJPOW2 | QGTEXI_CLAMP)
			? img->CopyToScale(_img_rsw)
			: img->CopyTo(_img_rsw);

		// 텍스쳐
		if (!_CreateInternal(&tex, dev))
			return false;

		_node_desc = (kuintptr)tex;

		// 업로드
		_UploadTexture(0);

		// 보관할 필요가 없으면 이미지 지움
		if (!K_OFLAG(_flags, QGTEXF_IMAGE))
		{
			_img_rsw->Unload();
			_img_rsw = NULL;
		}
	}

	// 오케
	return true;
}

//
bool DX9Texture::_UploadTexture(IDirect3DBaseTexture9* tex, kint mipno, qgImage* img, bool testautomip, bool isdyn, kint* mips)
{
	k_return_value_if_fail(img, false);

	// 텍스쳐 이미지 처리
	IDirect3DTexture9* tx = (IDirect3DTexture9*)tex;
	D3DLOCKED_RECT rt;
	HRESULT hr;

	if (SUCCEEDED(hr = tx->LockRect(mipno, &rt, NULL, isdyn ? D3DLOCK_DISCARD : 0)))
	{
		if (rt.Pitch == img->GetPitch())
			memcpy(rt.pBits, img->GetData(), img->GetSize());
		else
		{
			kint ip = img->GetPitch();
			kint ih = img->GetHeight();
			DWORD pitch = K_MIN(rt.Pitch, ip);

			const kbyte* src = (const kbyte*)img->GetData();
			kbyte* dst = (kbyte*)rt.pBits;

			for (kint i = 0; i < ih; i++)
			{
				memcpy(dst, src, pitch);
				src += ip;
				dst += rt.Pitch;
			}
		}

		tx->UnlockRect(mipno);
	}

	//
	if (testautomip && mipno == 0)
	{
		// 밉맵 자동
		tx->SetAutoGenFilterType(D3DTEXF_LINEAR);	// D3DTEXF_POINT 로 할까...
		tx->GenerateMipSubLevels();

		// 갯수 다시 얻기
		if (mips)
			*mips = (kint)tx->GetLevelCount();
	}

	return true;
}

//
bool DX9Texture::_UploadTexture(kint mipno)
{
	kint mips = _mips;	// 비트 필드이기 때문에 이렇게...

	bool ret = _UploadTexture((IDirect3DBaseTexture9*)_node_desc, mipno,
		mipno == 0 ? _img_rsw : _img_mip,
		K_OFLAG(_insts, QGTEXI_AUTOMIP), K_OFLAG(_dx_usage, D3DUSAGE_DYNAMIC), &mips);

	if (!ret)
		return false;
	else
	{
		_mips = mips;
		return true;
	}
}

//
bool DX9Texture::EnterMap(qgTextureMap* retdata, kint flag, kint mipno, qgTextureFace face)
{
	k_return_value_if_fail(retdata, false);
	k_return_value_if_fail(_lock_mip < 0, false);

	if ((kuint)mipno >= _mips)
	{
		kobj_tracep(KLV_ERROR, KERR_OVERFLOW);
		return false;
	}

	// DX9EX의 경우 DYNAMIC 지정안해도 DEFAULT DYNAMIC이므로 flag로 판단하면 안된다
	DWORD dw;

	if (K_OFLAG(_dx_usage, D3DUSAGE_DYNAMIC))
		dw = D3DLOCK_DISCARD;
	else if (K_OFLAG(flag, QGENTF_READ))
	{
		if (K_OFLAG(flag, QGENTF_WRITE))
		{
			// 헐... 이거 어케함...
			// 읽는 거 패스
			dw = 0;
		}
		else
			dw = D3DLOCK_READONLY;	// 만약 EX일 경우 읽지 못한다... UpdateTexture로 SYSMEM 으로 복사해야함
	}
	else
		dw = 0;

	// QGENTF_READWRITE인 경우는... 에이... 패스

	//
	HRESULT hr;

	switch (_textype)
	{
		case QGTEXTURE_1D:
		case QGTEXTURE_2D:
			if (face != QGTXFX_NONE)
				return false;
			else
			{
				IDirect3DTexture9* tx = (IDirect3DTexture9*)_node_desc;
				D3DLOCKED_RECT rt;

				if (FAILED(hr = tx->LockRect(mipno, &rt, NULL, dw)))
					return false;

				retdata->data = (kbyte*)rt.pBits;
				retdata->pitch = rt.Pitch;
				retdata->slice = 0;
			}
			break;

		case QGTEXTURE_3D:
			if (face != QGTXFX_NONE)
				return false;
			else
			{
				IDirect3DVolumeTexture9* tx = (IDirect3DVolumeTexture9*)_node_desc;
				D3DLOCKED_BOX bx;

				if (FAILED(hr = tx->LockBox(mipno, &bx, NULL, dw)))
					return false;

				retdata->data = (kbyte*)bx.pBits;
				retdata->pitch = bx.RowPitch;
				retdata->slice = bx.SlicePitch;
			}
			break;

		case QGTEXTURE_CUBE:
			if (face == QGTXFX_NONE)
				return false;
			else
			{
				IDirect3DCubeTexture9* tx = (IDirect3DCubeTexture9*)_node_desc;
				D3DLOCKED_RECT rt;

				if (FAILED(hr = tx->LockRect(DX9Convert::CubeMapFace(face), mipno, &rt, NULL, dw)))
					return false;

				retdata->data = (kbyte*)rt.pBits;
				retdata->pitch = rt.Pitch;
				retdata->slice = 0;
			}
			break;

		default:
			return false;
	}

	// 리턴값
	retdata->width = _awidth;
	retdata->height = _aheight;
	retdata->depth = _adepth;

	// 잠금 저장
	_lock_mip = mipno;
	_lock_face = face;

	return true;
}

//
bool DX9Texture::LeaveMap()
{
	k_return_value_if_fail(_lock_mip >= 0, false);

	HRESULT hr;

	switch (_textype)
	{
		case QGTEXTURE_1D:
		case QGTEXTURE_2D:
			if (FAILED(hr = ((IDirect3DTexture9*)_node_desc)->UnlockRect(_lock_mip)))
				return false;
			else if (_lock_mip == 0 && K_OFLAG(_insts, QGTEXI_AUTOMIP))
			{
				IDirect3DTexture9* tx = (IDirect3DTexture9*)_node_desc;
				tx->SetAutoGenFilterType(D3DTEXF_LINEAR);
				tx->GenerateMipSubLevels();
				_mips = (kint)tx->GetLevelCount();
			}
			break;

		case QGTEXTURE_3D:
			if (FAILED(hr = ((IDirect3DVolumeTexture9*)_node_desc)->UnlockBox(_lock_mip)))
				return false;
			else if (_lock_mip == 0 && K_OFLAG(_insts, QGTEXI_AUTOMIP))
			{
				IDirect3DVolumeTexture9* tx = (IDirect3DVolumeTexture9*)_node_desc;
				tx->SetAutoGenFilterType(D3DTEXF_LINEAR);
				tx->GenerateMipSubLevels();
				_mips = (kint)tx->GetLevelCount();
			}
			break;

		case QGTEXTURE_CUBE:
			if (FAILED(hr = ((IDirect3DCubeTexture9*)_node_desc)->UnlockRect(DX9Convert::CubeMapFace(_lock_face), _lock_mip)))
				return false;
			else if (_lock_mip == 0 && K_OFLAG(_insts, QGTEXI_AUTOMIP))
			{
				IDirect3DCubeTexture9* tx = (IDirect3DCubeTexture9*)_node_desc;
				tx->SetAutoGenFilterType(D3DTEXF_LINEAR);
				tx->GenerateMipSubLevels();
				_mips = (kint)tx->GetLevelCount();
			}
			break;

		default:
			return false;
	}

	// 정리
	_lock_mip = -1;
	_lock_face = QGTXFX_NONE;

	return true;
}

//
kint DX9Texture::Signal(const char* sig, kpointer arg)
{
	if (_dx_pool == D3DPOOL_DEFAULT)
	{
		if (k_streqv(sig, DX9_SIGNAL_LOST))
		{
			if (_node_desc)
			{
				IDirect3DBaseTexture9* tx = (IDirect3DBaseTexture9*)_node_desc;
				tx->Release();
				_node_desc = 0;
			}
		}
		else if (k_streqv(sig, DX9_SIGNAL_RESET))
		{
			DX9Device* dev = (DX9Device*)_dev;
			IDirect3DDevice9* device = dev->GetDevice();
			IDirect3DBaseTexture9* tx;
			HRESULT hr;

			switch (_textype)
			{
				case QGTEXTURE_1D:
				case QGTEXTURE_2D:
					hr = device->CreateTexture(_awidth, _aheight, _mips, _dx_usage, _dx_fmt, _dx_pool, (IDirect3DTexture9**)&tx, NULL);
					break;

				case QGTEXTURE_3D:
					hr = device->CreateVolumeTexture(_awidth, _aheight, _adepth, _mips, _dx_usage, _dx_fmt, _dx_pool, (IDirect3DVolumeTexture9**)&tx, NULL);
					break;

				case QGTEXTURE_CUBE:
					hr = device->CreateCubeTexture(_awidth, _mips, _dx_usage, _dx_fmt, _dx_pool, (IDirect3DCubeTexture9**)&tx, NULL);
					break;

				default:
					hr = E_FAIL;
					tx = NULL;
			}

			if (SUCCEEDED(hr))
				_node_desc = (kuintptr)tx;
			else
			{
				_node_desc = 0;
				return KERR_FAIL_CREATE;
			}
		}
	}

	return KERR_SUCCESS;
}


//////////////////////////////////////////////////////////////////////////
// 모션 텍스쳐
KIMPL_SUBCLASS(DX9TextureMotion, "DX9TextureMotion");

//
DX9TextureMotion::DX9TextureMotion() throw()
{
}

//
DX9TextureMotion::~DX9TextureMotion() throw()
{
	// 렌더러 마다 텍스쳐 해제가 다르므로 여기서 함
	if (_mt_preimgs.IsEmpty())
		CleanStreams();
	else
	{
		kForEach(kuintptr n, _mt_preimgs)
		{
			IDirect3DBaseTexture9* tx = (IDirect3DBaseTexture9*)n;
			tx->Release();
		}
	}
}

//
kint DX9TextureMotion::Signal(const char* sig, kpointer arg)
{
	if (_dx_pool == D3DPOOL_DEFAULT && _mt_preimgs.IsEmpty())
	{
		if (k_streqv(sig, DX9_SIGNAL_LOST))
		{
			if (_node_desc)
			{
				IDirect3DBaseTexture9* tx = (IDirect3DBaseTexture9*)_node_desc;
				tx->Release();
				_node_desc = 0;
			}
		}
		else if (k_streqv(sig, DX9_SIGNAL_RESET))
		{
			DX9Device* dev = (DX9Device*)_dev;
			IDirect3DDevice9* device = dev->GetDevice();
			IDirect3DBaseTexture9* tx;

			if (SUCCEEDED(device->CreateTexture(_awidth, _aheight, _mips, _dx_usage, _dx_fmt, _dx_pool, (IDirect3DTexture9**)&tx, NULL)))
				_node_desc = (kuintptr)tx;
			else
			{
				_node_desc = 0;
				return KERR_FAIL_CREATE;
			}
		}
	}

	return KERR_SUCCESS;
}

//
qgITextureMotion* DX9TextureMotion::GetMotion()
{
	return this;
}

//
bool DX9TextureMotion::MotionUpdate(float adv)
{
	k_return_value_if_fail(_lock_mip < 0, false);

	//
	kuintptr ptr;
	bool res;
	IDirect3DTexture9* tx;

	if (!ManualUpdate(adv, res, ptr))
		return false;

	if (res)
	{
		// 다이나믹으로 만들었으니 걍 처리
		tx = (IDirect3DTexture9*)_node_desc;

		D3DLOCKED_RECT rt;

		if (FAILED(tx->LockRect(0, &rt, NULL, D3DLOCK_DISCARD)))
			return false;

		qgImage* img = _mt_ucmimage;

		if (img->GetPitch() == (kint)rt.Pitch)
			memcpy(rt.pBits, img->GetData(), img->GetSize());
		else
		{
			// 피치는 각 대상의 크기 단위라는거 주의
			// 무조건 32비트 RGBA만 사용함
			qgCcmWork::COPY_32_32(_width, _height, (kint*)img->GetData(), 0, (kint*)rt.pBits, rt.Pitch / 4, false);
		}

		tx->UnlockRect(0);
	}
	else if (ptr != 0)
	{
		if (ptr != _node_desc)
		{
			if (_node_desc)
			{
				tx = (IDirect3DTexture9*)_node_desc;
				tx->Release();
			}

			_node_desc = ptr;

			if (ptr)
			{
				tx = (IDirect3DTexture9*)ptr;
				tx->AddRef();
			}
		}
	}

	return true;
}

//
bool DX9TextureMotion::_CreateMotion(const char* filename, kint flags, DX9Device* dev)
{
	// 스트림
	kStream* stm = dev->FileOpen(filename, true);
	if (stm == NULL)
		return NULL;

	if (!qgFwSupp::CheckHeadAndTail(stm))
	{
		stm->Unload();
		return NULL;
	}

	// 모션
	if (!ParseMotion(stm, K_OFLAG(flags, QGTEXF_PRELOAD)))
		return false;

	// DX9과의 호환성을 위해서는 RGBA -> BGRA를 쓰는 것이 좋다... 지만 먼저 DX9부터 고쳐야함
	qgImage* img = _mt_ucmimage;

	if (_mt_preimgs.IsEmpty())
	{
		// 한장 갖다 잠궈 씀
		if (!_InitProperty(QGTEXTURE_2D, filename, img->GetWidth(), img->GetHeight(), 1, 1, QGTEXF_S2D | QGRESF_DYNAMIC | QGTEXF_MOTION, QGCF32_RGBA, dev) ||
			!_CreateTexture(NULL, dev))
			return false;

		// 실제 텍스쳐 사이즈랑 다르면 버퍼를 다시 만들어야함 -> 잠그기용
		if (K_OFLAG(_insts, QGTEXI_ADJPOW2 | QGTEXI_ADJSQR))
			_img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
	}
	else
	{
		// 모두 만들어 놓기
		if (!_InitProperty(QGTEXTURE_2D, filename, img->GetWidth(), img->GetHeight(), 1, 1, QGTEXF_S2D | QGTEXF_MOTION, QGCF32_RGBA, dev))
			return false;

		// 실제 텍스쳐 사이즈랑 다르면 버퍼를 다시 만들어야함 -> 잠그기용
		qgImage* imgexp;

		if (K_OFLAG(_insts, QGTEXI_ADJPOW2 | QGTEXI_ADJSQR) == 0)
			imgexp = NULL;
		else
		{
			_img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
			imgexp = _img_rsw;
		}

		//
		IDirect3DTexture9* tx = NULL;

		for (kuint i = 0; i < _mt_count; i++)
		{
			if (!_CreateInternal((IDirect3DBaseTexture9**)&tx, dev))
			{
				// 오류있는 프레임은 패스
				tx = NULL;
			}
			else
			{
				if (!UpdateStreamImage(i))
				{
					tx->Release();
					tx = NULL;
				}
				else
				{
					if (imgexp)
					{
						// 이미지 늘려서 그리기
						qgBlitImage::StrechBlit(QG_BLITOP_TEX, imgexp, NULL, img, NULL, tpl::ColorValues::Transparent);
						_UploadTexture(tx, 0, imgexp, false, true, NULL);
					}
					else
					{
						// 무조건 32비트 RGBA
						_UploadTexture(tx, 0, img, false, true, NULL);
					}
				}
			}

			_mt_preimgs[i] = (kuintptr)tx;
		}

		// 0번 넣어 둠
		tx = (IDirect3DTexture9*)_mt_preimgs[0];
		kunk_add_ref(tx);

		_node_desc = (kuintptr)tx;

		// 정리
		CleanStreams();

		K_YFLAG(_insts, QGTEXI_LIST, true);
	}

	// 오케
	return true;
}


//////////////////////////////////////////////////////////////////////////
// 오프 텍스쳐
KIMPL_SUBCLASS(DX9TextureOff, "DX9TextureOff");


//
DX9TextureOff::DX9TextureOff() throw()
{
}

//
DX9TextureOff::~DX9TextureOff() throw()
{
	kunk_release(_surface);
	kunk_release(_lsf);

	if (_db)
		_db->Unload();
}

//
kint DX9TextureOff::Signal(const char* sig, kpointer arg)
{
	if (k_streqv(sig, DX9_SIGNAL_LOST))
	{
		if (_node_desc)
		{
			IDirect3DBaseTexture9* tx = (IDirect3DBaseTexture9*)_node_desc;
			tx->Release();
			_node_desc = 0;
		}

		kunk_reset(&_surface);
		kunk_reset(&_lsf);

		kobj_reset(&_db);
	}
	else if (k_streqv(sig, DX9_SIGNAL_RESET))
	{
		DX9Device* dev = (DX9Device*)_dev;
		IDirect3DDevice9* device = dev->GetDevice();
		IDirect3DTexture9* tx;

		if (FAILED(device->CreateTexture(_awidth, _aheight, _mips, _dx_usage, _dx_fmt, _dx_pool, &tx, NULL)))
		{
			_node_desc = 0;
			return KERR_FAIL_CREATE;
		}

		_node_desc = (kuintptr)tx;

		if (!_CreateOff(dev))
		{
			tx->Release();
			_node_desc = 0;
			return KERR_FAIL_CREATE;
		}
	}

	return KERR_SUCCESS;
}

//
qgITextureOff* DX9TextureOff::GetOff()
{
	return this;
}

//
bool DX9TextureOff::_CreateOff(DX9Device* dev)
{
	IDirect3DSurface9* surface = NULL;
	HRESULT hr = E_FAIL;

	switch (_textype)
	{
		case QGTEXTURE_1D:
		case QGTEXTURE_2D:
			hr = ((IDirect3DTexture9*)_node_desc)->GetSurfaceLevel(0, &surface);
			break;

		case QGTEXTURE_3D:
			// 지원 안함
			break;

		case QGTEXTURE_CUBE:
			// 어케해야되나...
			//hr = ((IDirect3DCubeTexture9*)_node_desc)->GetCubeMapSurface(0, 0, &surface);
			break;

		default:
			break;
	}

	if (FAILED(hr))
	{
		kobj_tracet(KLV_ERROR, KERR_FAIL_CREATE, "render target surface error.");
		return false;
	}

	//
	_surface = surface;

	return true;
}

// 오직 1D, 2D만 지원
bool DX9TextureOff::EnterMap(qgTextureMap* retdata, kint flag, kint mipno, qgTextureFace face)
{
	k_return_value_if_fail(retdata, false);
	k_return_value_if_fail(_lock_mip < 0, false);
	k_return_value_if_fail(mipno == 0, false);
	k_return_value_if_fail(face == QGTXFX_NONE, false);

	DX9Device* dev = (DX9Device*)_dev;
	IDirect3DDevice9* device = dev->GetDevice();

	if (!_lsf)
	{
		if (FAILED(device->CreateOffscreenPlainSurface(_awidth, _aheight, _dx_fmt, D3DPOOL_SYSTEMMEM, &_lsf, NULL)))
		{
			kobj_tracet(KLV_ERROR, KERR_NOLOCK, "cannot create offscreen surface.");
			return false;
		}
	}

	if (FAILED(device->GetRenderTargetData(_surface, _lsf)))
	{
		kobj_tracet(KLV_ERROR, KERR_NOLOCK, "cannot create lock area for off texture.");
		return false;
	}

	D3DLOCKED_RECT rt;
	if (FAILED(_lsf->LockRect(&rt, NULL, flag == QGENTF_READ ? D3DLOCK_READONLY : 0)))
	{
		kobj_tracet(KLV_ERROR, KERR_NOLOCK, "cannot lock off texture.");
		return false;
	}

	// 리턴값
	retdata->data = (kbyte*)rt.pBits;
	retdata->pitch = rt.Pitch;
	retdata->slice = 0;

	retdata->width = _awidth;
	retdata->height = _aheight;
	retdata->depth = _adepth;

	// 잠금 저장
	_lock_mip = 0;
	_lock_face = QGTXFX_NONE;

	return true;
}

// 오직 1D, 2D만 지원
bool DX9TextureOff::LeaveMap()
{
	k_return_value_if_fail(_lock_mip == 0, false);
	k_return_value_if_fail(_lsf, false);

	_lsf->UnlockRect();

	// 정리
	_lock_mip = -1;

	return true;
}

bool DX9TextureOff::EnterOff(kint clearance, const tpl::ColorValue& color)
{
	if (K_OFLAG(_insts, QGTEXI_LOCKOFF))
		return false;

	DX9Device* dev = (DX9Device*)_dev;

	// 깊이 버퍼
	DX9DepthBuffer* db = _db;
	if (db == NULL)
		_db = db = (DX9DepthBuffer*)_dev->CreateDepthBuffer(_awidth, _aheight);

	//
	dev->_SetRenderTarget(this);

	// 설정
	D3DVIEWPORT9 vp;
	vp.X = 0;
	vp.Y = 0;
	vp.Width = _awidth;
	vp.Height = _aheight;
	vp.MinZ = 0.0f;
	vp.MaxZ = 1.0f;

	IDirect3DDevice9* device = dev->GetDevice();
	IDirect3DSurface9* dsb = db ? (IDirect3DSurface9*)db->GetDescription() : NULL;

	device->SetRenderTarget(0, _surface);
	device->SetDepthStencilSurface(dsb);
	device->SetViewport(&vp);

	//
	K_YFLAG(_insts, QGTEXI_LOCKOFF, true);

	return dev->_InternalEnter(clearance, tpl::Color(color), 0, 1.0f);
}

bool DX9TextureOff::LeaveOff()
{
	if (!K_OFLAG(_insts, QGTEXI_LOCKOFF))
		return false;

	DX9Device* dev = (DX9Device*)_dev;
	bool ret = dev->_InternalLeave();

	dev->_SetRenderTarget(NULL);

	K_YFLAG(_insts, QGTEXI_LOCKOFF, false);

	return ret;
}


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
KIMPL_SUBCLASS(DX9DepthBuffer, "DX9DepthBuffer");

//
DX9DepthBuffer::DX9DepthBuffer() throw()
{
}

//
DX9DepthBuffer::~DX9DepthBuffer() throw()
{
	if (_node_desc != 0)
	{
		IDirect3DSurface9* dsb = (IDirect3DSurface9*)_node_desc;
		dsb->Release();
	}
}

//
kint DX9DepthBuffer::Signal(const char* sig, kpointer arg)
{
	if (k_streqv(sig, DX9_SIGNAL_LOST))
	{
		if (_node_desc)
		{
			IDirect3DSurface9* dsb = (IDirect3DSurface9*)_node_desc;
			dsb->Release();
			_node_desc = 0;
		}
	}
	else if (k_streqv(sig, DX9_SIGNAL_RESET))
	{
		DX9Device* dev = (DX9Device*)_dev;
		IDirect3DDevice9* device = dev->GetDevice();
		HRESULT hr;

		//
		IDirect3DTexture9* tds;
		hr = device->CreateTexture(_width, _height, 1, D3DUSAGE_DEPTHSTENCIL, dev->GetDepthStencilFormat(), D3DPOOL_DEFAULT, &tds, NULL);

		if (FAILED(hr))
		{
			_node_desc = 0;
			return KERR_FAIL_CREATE;
		}

		//
		IDirect3DSurface9* dsb;
		hr = tds->GetSurfaceLevel(0, &dsb);

		tds->Release();	// 이걸 놔두면 텍스쳐로 그릴 수 있는걸까

		if (FAILED(hr))
		{
			_node_desc = 0;
			return KERR_FAIL_CREATE;
		}

		//
		_node_desc = K_CAST_PTR_TO_PUINT(dsb);
	}

	return KERR_SUCCESS;
}

//
bool DX9DepthBuffer::_Create(kuint width, kuint height, DX9Device* dev)
{
	IDirect3DDevice9* device = dev->GetDevice();

	//
	IDirect3DTexture9* tds;
	HRESULT hr = device->CreateTexture(width, height, 1, D3DUSAGE_DEPTHSTENCIL, dev->GetDepthStencilFormat(), D3DPOOL_DEFAULT, &tds, NULL);

	if (FAILED(hr))
	{
		kobj_tracet(KLV_ERROR, KERR_FAIL_CREATE, "depth stencil texture error.");
		return false;
	}

	//
	IDirect3DSurface9* dsb;
	hr = tds->GetSurfaceLevel(0, &dsb);

	tds->Release();	// 이걸 놔두면 텍스쳐로 그릴 수 있는걸까

	if (FAILED(hr))
	{
		kobj_tracet(KLV_ERROR, KERR_FAIL_CREATE, "depth stencil surface error.");
		return false;
	}

	//
	_node_desc = K_CAST_PTR_TO_PUINT(dsb);
	_width = width;
	_height = height;

	return true;
}

//
bool DX9Texture::_MakeCubeTex(IDirect3DCubeTexture9* tex, DX9Device* dev)
{
	_InitProperty(QGTEXTURE_CUBE, NULL, 256, 256, 256, 0, 0, QGCF32_RGBA, dev);
	_node_desc = (kuintptr)tex;

	return true;
}

//
extern "C"
KAPI kpointer qg_cube_map(qgDevice* pdev, const kwchar* filename)
{
	DX9Device* dev = (DX9Device*)pdev;

	IDirect3DCubeTexture9* tex;
	HRESULT hr = DX9Device::DXCREATECUBEFROMFILEEX(
		dev->GetDevice(), filename, 0, 0, 0,
		D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT,
		0, NULL, NULL, &tex);

	if (FAILED(hr))
		return NULL;

	DX9Texture* self = kobj_create(DX9Texture);
	self->_MakeCubeTex(tex, dev);
	dev->_RoamLoad(QGROAM_RES_5, self);

	return self;
}

#endif	// _QG_USE_DX9

